#include "wx/wxprec.h"
#include "wx/apptrait.h"
#include "wx/cmdline.h"
#include "wx/confbase.h"
#include "wx/filename.h"
#include "wx/msgout.h"
#include "wx/tokenzr.h"
#include  <signal.h>
#include "wx/fontmap.h"

#ifdef __WXDEBUG__
#include "wx/stackwalk.h"
#include "wx/recguard.h"
#endif


#ifdef __WXDEBUG__
static bool DoShowAssertDialog( const wxString& msg );

static void ShowAssertDialog( const wxChar *szFile, int nLine, const wxChar *szFunc, const wxChar *szCond,
                              const wxChar *szMsg, wxAppTraits *traits = NULL );

static void LINKAGEMODE SetTraceMasks();
#endif

wxAppConsole *wxAppConsole::ms_appInstance = NULL;
wxAppInitializerFunction wxAppConsole::ms_appInitFn = NULL;

wxAppConsole::wxAppConsole() {
  m_traits = NULL;
  ms_appInstance = this;
  #ifdef __WXDEBUG__
  SetTraceMasks();
  delete m_traits;
  m_traits = NULL;
  #endif
}

wxAppConsole::~wxAppConsole() {
  delete m_traits;
}

bool wxAppConsole::Initialize( int& argcOrig, wxChar **argvOrig ) {
  argc = argcOrig;
  argv = argvOrig;
  #ifndef __WXPALMOS__
  if( m_appName.empty() && argv ) {
    wxFileName::SplitPath( argv[0], NULL, &m_appName, NULL );
  }
  #endif
  return true;
}

void wxAppConsole::CleanUp() {
}

bool wxAppConsole::OnInit() {
  #if wxUSE_CMDLINE_PARSER
  wxCmdLineParser parser( argc, argv );
  OnInitCmdLine( parser );
  bool cont;
  switch( parser.Parse( false /* don't show usage */ ) ) {
    case -1:
      cont = OnCmdLineHelp( parser );
      break;
    case 0:
      cont = OnCmdLineParsed( parser );
      break;
    default:
      cont = OnCmdLineError( parser );
      break;
  }
  if( !cont ) {
    return false;
  }
  #endif
  return true;
}

int wxAppConsole::OnExit() {
  #if wxUSE_CONFIG
  delete wxConfigBase::Set( ( wxConfigBase * ) NULL );
  #endif
  return 0;
}

void wxAppConsole::Exit() {
  exit( -1 );
}

wxAppTraits *wxAppConsole::CreateTraits() {
  return new wxConsoleAppTraits;
}

wxAppTraits *wxAppConsole::GetTraits() {
  if( !m_traits ) {
    m_traits = CreateTraits();
    wxASSERT_MSG( m_traits, _T( "wxApp::CreateTraits() failed?" ) );
  }
  return m_traits;
}

void wxAppConsole::ProcessPendingEvents() {
  #if wxUSE_THREADS
  if( !wxPendingEventsLocker ) {
    return;
  }
  #endif
  wxENTER_CRIT_SECT( *wxPendingEventsLocker );
  if( !wxPendingEvents ) {
    wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
    return;
  }
  wxList::compatibility_iterator node = wxPendingEvents->GetFirst();
  while( node ) {
    wxEvtHandler *handler = ( wxEvtHandler * )node->GetData();
    wxPendingEvents->Erase( node );
    wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
    handler->ProcessPendingEvents();
    wxENTER_CRIT_SECT( *wxPendingEventsLocker );
    node = wxPendingEvents->GetFirst();
  }
  wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
}

int wxAppConsole::FilterEvent( wxEvent & ( event ) ) {
  return -1;
}

#if wxUSE_EXCEPTIONS

void
wxAppConsole::HandleEvent( wxEvtHandler *handler,wxEventFunction func,wxEvent& event ) const {
  ( handler->*func )( event );
}

#endif

#if wxUSE_CMDLINE_PARSER

#define OPTION_VERBOSE _T("verbose")

void wxAppConsole::OnInitCmdLine( wxCmdLineParser& parser ) {
  static const wxCmdLineEntryDesc cmdLineDesc[] = {
    {
      wxCMD_LINE_SWITCH,
      _T( "h" ),
      _T( "help" ),
      gettext_noop( "show this help message" ),
      wxCMD_LINE_VAL_NONE,
      wxCMD_LINE_OPTION_HELP
    },

    #if wxUSE_LOG
    {
      wxCMD_LINE_SWITCH,
      wxEmptyString,
      OPTION_VERBOSE,
      gettext_noop( "generate verbose log messages" ),
      wxCMD_LINE_VAL_NONE,
      0x0
    },
    #endif

    {
      wxCMD_LINE_NONE,
      wxEmptyString,
      wxEmptyString,
      wxEmptyString,
      wxCMD_LINE_VAL_NONE,
      0x0
    }
  };
  parser.SetDesc( cmdLineDesc );
}

bool wxAppConsole::OnCmdLineParsed( wxCmdLineParser& parser ) {
  #if wxUSE_LOG
  if( parser.Found( OPTION_VERBOSE ) ) {
    wxLog::SetVerbose( true );
  }
  #else
  wxUnusedVar( parser );
  #endif
  return true;
}

bool wxAppConsole::OnCmdLineHelp( wxCmdLineParser& parser ) {
  parser.Usage();
  return false;
}

bool wxAppConsole::OnCmdLineError( wxCmdLineParser& parser ) {
  parser.Usage();
  return false;
}

#endif

#ifdef __WXDEBUG__

void wxAppConsole::OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg ) {
  ShowAssertDialog( file, line, func, cond, msg, GetTraits() );
}

void wxAppConsole::OnAssert( const wxChar *file, int line, const wxChar *cond, const wxChar *msg ) {
  OnAssertFailure( file, line, NULL, cond, msg );
}

#endif

#if wxUSE_LOG

wxLog *wxConsoleAppTraitsBase::CreateLogTarget() {
  return new wxLogStderr;
}

#endif

wxMessageOutput *wxConsoleAppTraitsBase::CreateMessageOutput() {
  return new wxMessageOutputStderr;
}

wxFontMapper *wxConsoleAppTraitsBase::CreateFontMapper() {
  return ( wxFontMapper * )new wxFontMapperBase;
}

wxRendererNative *wxConsoleAppTraitsBase::CreateRenderer() {
  return NULL;
}

#ifdef __WXDEBUG__
bool wxConsoleAppTraitsBase::ShowAssertDialog( const wxString& msg ) {
  return wxAppTraitsBase::ShowAssertDialog( msg );
}
#endif

void wxConsoleAppTraitsBase::ScheduleForDestroy( wxObject *object ) {
  delete object;
}

void wxConsoleAppTraitsBase::RemoveFromPendingDelete( wxObject * ( object ) ) {
}

#if wxUSE_SOCKETS
GSocketGUIFunctionsTable* wxConsoleAppTraitsBase::GetSocketGUIFunctionsTable() {
  return NULL;
}
#endif


#ifdef __WXDEBUG__

bool wxAppTraitsBase::ShowAssertDialog( const wxString& msgOriginal ) {
  wxString msg = msgOriginal;
  #if wxUSE_STACKWALKER
  #if !defined(__WXMSW__)
  wxFprintf( stderr, wxT( "[Debug] Generating a stack trace... please wait" ) );
  fflush( stderr );
  #endif
  const wxString stackTrace = GetAssertStackTrace();
  if( !stackTrace.empty() ) {
    msg << _T( "\n\nCall stack:\n" ) << stackTrace;
  }
  #endif
  return DoShowAssertDialog( msg );
}

wxString wxAppTraitsBase::GetAssertStackTrace() {
  wxString stackTrace;
  class StackDump : public wxStackWalker {
    public:
      StackDump() { }
      const wxString& GetStackTrace() const { return m_stackTrace; }
    protected:
      virtual void OnStackFrame( const wxStackFrame& frame ) {
        m_stackTrace << wxString::Format( _T( "[%02d] " ), wx_truncate_cast( int, frame.GetLevel() ) );
        wxString name = frame.GetName();
        if( !name.empty() ) {
          m_stackTrace << wxString::Format( _T( "%-40s" ), name.c_str() );
        } else
        { m_stackTrace << wxString::Format( _T( "%p" ), frame.GetAddress() ); }
        if( frame.HasSourceLocation() ) {
          m_stackTrace << _T( '\t' )
                       << frame.GetFileName()
                       << _T( ':' )
                       << frame.GetLine();
        }
        m_stackTrace << _T( '\n' );
      }

    private:
      wxString m_stackTrace;
  };
  static const int maxLines = 20;
  StackDump dump;
  dump.Walk( 2, maxLines );
  stackTrace = dump.GetStackTrace();
  const int count = stackTrace.Freq( wxT( '\n' ) );
  for( int i = 0; i < count - maxLines; i++ ) {
    stackTrace = stackTrace.BeforeLast( wxT( '\n' ) );
  }
  return stackTrace;
}

#endif

void wxExit() {
  if( wxTheApp ) {
    wxTheApp->Exit();
  } else {
    exit( -1 );
  }
}

void wxWakeUpIdle() {
  if( wxTheApp ) {
    wxTheApp->WakeUpIdle();
  }
}

#ifdef  __WXDEBUG__
bool wxAssertIsEqual( int x, int y ) {
  return x == y;
}


void wxTrap() {
  #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
  DebugBreak();
  #elif defined(__WXMAC__) && !defined(__DARWIN__)
  #if __powerc
  Debugger();
  #else
  SysBreak();
  #endif
  #elif defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
  Debugger();
  #elif defined(__UNIX__)
  raise( SIGTRAP );
  #else
  #endif
}


void wxOnAssert( const wxChar *szFile,
                 int nLine,
                 const char *szFunc,
                 const wxChar *szCond,
                 const wxChar *szMsg ) {
  static int s_bInAssert = 0;
  wxRecursionGuard guard( s_bInAssert );
  if( guard.IsInside() ) {
    wxTrap();
    return;
  }
  const wxString strFunc = wxString::FromAscii( szFunc );
  if( !wxTheApp ) {
    ShowAssertDialog( szFile, nLine, strFunc, szCond, szMsg );
  } else {
    wxTheApp->OnAssertFailure( szFile, nLine, strFunc, szCond, szMsg );
  }
}

#endif

#ifdef __WXDEBUG__

static void LINKAGEMODE SetTraceMasks() {
  #if wxUSE_LOG
  wxString mask;
  if( wxGetEnv( wxT( "WXTRACE" ), &mask ) ) {
    wxStringTokenizer tkn( mask, wxT( ",;:" ) );
    while( tkn.HasMoreTokens() ) {
      wxLog::AddTraceMask( tkn.GetNextToken() );
    }
  }
  #endif
}

bool DoShowAssertDialog( const wxString& msg ) {
  #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
  wxString msgDlg( msg );
  msgDlg += wxT( "\nDo you want to stop the program?\n" )
            wxT( "You can also choose [Cancel] to suppress " )
            wxT( "further warnings." );
  switch( ::MessageBox( NULL, msgDlg, _T( "wxWidgets Debug Alert" ),
                        MB_YESNOCANCEL | MB_ICONSTOP ) ) {
    case IDYES:
      wxTrap();
      break;
    case IDCANCEL:
      return true;
  }
  #else
  wxFprintf( stderr, wxT( "%s\n" ), msg.c_str() );
  fflush( stderr );
  wxTrap();
  #endif
  return false;
}


static
void ShowAssertDialog( const wxChar *szFile,
                       int nLine,
                       const wxChar *szFunc,
                       const wxChar *szCond,
                       const wxChar *szMsg,
                       wxAppTraits *traits ) {
  static bool s_bNoAsserts = false;
  wxString msg;
  msg.reserve( 2048 );
  msg.Printf( wxT( "%s(%d): assert \"%s\" failed" ), szFile, nLine, szCond );
  if( szFunc && *szFunc ) {
    msg << _T( " in " ) << szFunc << _T( "()" );
  }
  if( szMsg ) {
    msg << _T( ": " ) << szMsg;
  } else
  { msg << _T( '.' ); }
  #if wxUSE_THREADS
  if( !wxThread::IsMain() ) {
    msg += wxT( " [in child thread]" );
    #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
    msg << wxT( "\r\n" );
    OutputDebugString( msg );
    #else
    wxFprintf( stderr, wxT( "%s\n" ), msg.c_str() );
    fflush( stderr );
    #endif
    wxTrap();
  } else
  #endif
    if( !s_bNoAsserts ) {
      wxLogDebug( _T( "%s" ), msg.c_str() );
      if( traits ) {
        s_bNoAsserts = traits->ShowAssertDialog( msg );
      } else {
        s_bNoAsserts = DoShowAssertDialog( msg );
      }
    }
}

#endif
